package com.yunquan.ohana.ui.calendar.task

import android.app.Application
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableField
import androidx.lifecycle.LifecycleOwner
import com.google.gson.Gson
import com.yunquan.ohana.BR
import com.yunquan.ohana.R
import com.yunquan.ohana.base.binding.command.BindingAction
import com.yunquan.ohana.base.binding.command.BindingCommand
import com.yunquan.ohana.base.bus.RxBus
import com.yunquan.ohana.base.bus.event.SingleLiveEvent
import com.yunquan.ohana.base.ui.BaseViewModel
import com.yunquan.ohana.db.DBFrames
import com.yunquan.ohana.dialog.LoadingDialog
import com.yunquan.ohana.entity.EventEntity
import com.yunquan.ohana.entity.EventType
import com.yunquan.ohana.entity.FramesDataManager
import com.yunquan.ohana.entity.FramesEntity
import com.yunquan.ohana.entity.LocalFrameEntity
import com.yunquan.ohana.entity.RoleEntity
import com.yunquan.ohana.entity.TaskEntity
import com.yunquan.ohana.entity.TaskFilterDto
import com.yunquan.ohana.http.BaseHttp
import com.yunquan.ohana.http.exception.ApiException
import com.yunquan.ohana.http.observer.BaseObserver
import com.yunquan.ohana.http.transformer.CommonTransformer
import com.yunquan.ohana.ui.calendar.task.add.TaskAddActivity
import com.yunquan.ohana.ui.calendar.task.filter.TaskFilterActivity
import com.yunquan.ohana.utils.StringUtils
import com.yunquan.ohana.utils.ToastUtils
import com.yunquan.ohana.utils.Utils
import io.reactivex.rxjava3.disposables.Disposable
import me.tatarka.bindingcollectionadapter2.ItemBinding
import okhttp3.RequestBody.Companion.toRequestBody
import java.time.LocalDate
import java.time.LocalTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter

class TaskViewModel(application: Application) : BaseViewModel(application) {

    val mProgress = SingleLiveEvent<Int>()
    val mShowDate = ObservableField<String>()
    val mCurrentRoleName = ObservableField<String>()
    val mShowLauncher = ObservableField<Boolean>()
    val mProgressText = ObservableField<String>("0/0")
    val roleItems = ObservableArrayList<TaskRoleItemViewModel>()
    val roleItemBinding = ItemBinding.of<TaskRoleItemViewModel>(
        BR.taskRoleItemViewModel,
        R.layout.item_task_role,
    )

    val items = ObservableArrayList<TaskItemViewModel>()
    val itemBinding = ItemBinding.of<TaskItemViewModel>(
        BR.taskItemViewModel,
        R.layout.item_task_list,
    )

    var mDeviceId: String? = null
    var mUserType: Boolean = false
    var mRoleId: String? = null
    var mFilterBean: TaskFilterDto? = null
    var mCurrentRole: RoleEntity? = null

    var mCurrentDate: LocalDate = LocalDate.now()
    private val mFormat = DateTimeFormatter.ofPattern("EEE, MMM d")

    val onPreviousClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            mCurrentDate = mCurrentDate.minusDays(1)
            mShowDate.set(mCurrentDate.format(mFormat))
            getTaskList()
        }
    })

    val onNextClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            mCurrentDate = mCurrentDate.plusDays(1)
            mShowDate.set(mCurrentDate.format(mFormat))
            getTaskList()
        }
    })

    val onTodayClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            mCurrentDate = LocalDate.now()
            mShowDate.set(mCurrentDate.format(mFormat))
            getTaskList()
        }
    })

    val onFilterClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            if (mFilterBean == null) {
                mFilterBean = TaskFilterDto(
                    completed = true,
                    lateChores = true,
                    roleList = roleItems.map { it.data!! }
                )
            }
            val bundle = Bundle()
            bundle.putString("deviceId", mDeviceId)
            bundle.putBoolean("userType", mUserType)
            bundle.putString("data", Gson().toJson(mFilterBean))
            startActivityResult(TaskFilterActivity::class.java, bundle)
        }
    })

    val onAddTaskClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val bundle = Bundle()
            bundle.putString("roleId", mRoleId)
            bundle.putString("deviceId", mDeviceId)
            bundle.putBoolean("userType", mUserType)
            startActivityResult(TaskAddActivity::class.java, bundle)
        }
    })

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        mShowDate.set(mCurrentDate.format(mFormat))
        mCurrentRoleName.set(Utils.instance.getString(R.string.all_profiles))
        getProfilesList()
        getTaskList()
    }

    fun onChangeRole(data: RoleEntity?) {
        mCurrentRole = data
        roleItems.forEach {
            if (data != null && data.id == it.data!!.id) {
                it.setChecked(true)
            } else {
                it.setChecked(false)
            }
        }
        if (data != null) {
            mCurrentRoleName.set(data.account)
        } else {
            mCurrentRoleName.set(Utils.instance.getString(R.string.all_profiles))
        }
        if (mFilterBean != null && data != null) {
            mFilterBean?.roleList = arrayListOf(data)
        }
        getTaskList()
    }

    fun onDeleteTask(data: TaskEntity) {
        val map = mutableMapOf<String, Any>()
        map["deviceId"] = mDeviceId!!
        map["tasksId"] = data.id!!
        map["roleIds"] = if (mCurrentRole != null) listOf(mCurrentRole!!.id) else data.tasksToRoleVos!!.map { it.roleId }.toList()
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.deleteTasks(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        getTaskList()
                    } else {
                        ToastUtils.showShort(Utils.instance.getString(R.string.task_deletion_failed))
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }

    fun refreshData() {
        mCurrentRole = null
        roleItems.clear()
        mFilterBean?.roleList?.forEach {
            roleItems.add(TaskRoleItemViewModel(this@TaskViewModel, it))
        }
        roleItems.forEach { it.setChecked(false) }
        mCurrentRoleName.set(Utils.instance.getString(R.string.all_profiles))
        getTaskList()
    }

    fun getTaskList() {
        val map = mutableMapOf<String, Any>()
        if (mFilterBean != null) {
            if (mCurrentRole != null) {
                map["roleIds"] = arrayListOf(mCurrentRole!!.id!!)
            } else {
                map["roleIds"] = mFilterBean!!.roleList.map { it.id }.toList()
            }
            if (!mFilterBean!!.completed || !mFilterBean!!.lateChores) {
                map["status"] = if (mFilterBean!!.completed) 1 else 0
            }
        } else if (mCurrentRole != null) {
            map["roleIds"] = arrayListOf(mCurrentRole!!.id!!)
        }
        map["deviceId"] = mDeviceId!!
        map["startDate"] = mCurrentDate.atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond()
        map["endDate"] = mCurrentDate.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toEpochSecond()
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getTasksByDeviceId(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<TaskEntity>>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: List<TaskEntity>?) {
                    super.onSuccess(result)
                    items.clear()
                    if (result != null) {
                        val meRole = roleItems.firstOrNull()?.data
                        var count = 0
                        for (e in result) {
                            if (e.status == 1.0) {
                                count++
                            }
                            items.add(
                                TaskItemViewModel(
                                    this@TaskViewModel,
                                    e,
                                    mCurrentRole != null && meRole?.id == mCurrentRole?.id
                                )
                            )
                        }
                        mProgressText.set("$count/${result.size}")
                        if (count == 0) {
                            mProgress.value = 0
                        } else {
                            val v = count.toFloat() / result.size.toFloat() * 100.0
                            mProgress.value = v.toInt()
                        }
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }

    private fun getProfilesList() {
        val map = mutableMapOf<String, Any>()
        map["deviceId"] = mDeviceId!!
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getDeviceRoleListById(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<RoleEntity>>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: List<RoleEntity>?) {
                    super.onSuccess(result)
                    roleItems.clear()
                    result?.forEach {
                        roleItems.add(TaskRoleItemViewModel(this@TaskViewModel, it))
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }


    fun bindFrames(code: String) {
        val map = mutableMapOf<String, Any>()
        map["qrcode"] = code
        val json = Gson().toJson(map)
        val body = json.toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.bindDevice(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    LoadingDialog.getInstance().show()
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        getFrames()
                    } else {
                        ToastUtils.showShort(R.string.bind_failed)
                        LoadingDialog.getInstance().dismiss()
                    }
                }

                override fun onError(e: ApiException) {
                    super.onError(e)
                    LoadingDialog.getInstance().dismiss()
                }
            })
    }

    fun getFrames() {
        val map = mutableMapOf<String, Any>()
        val json = Gson().toJson(map)
        val body = json.toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getUserDeviceList(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<FramesEntity>>() {
                override fun onSuccess(result: List<FramesEntity>?) {
                    super.onSuccess(result)
                    if (result?.isNotEmpty() == true) {
                        FramesDataManager.instance.setData(result)
                        for (i in result.indices) {
                            val e = result[i]
                            val bean = LocalFrameEntity(
                                deviceId = e.id,
                                name = e.name,
                                location = e.roomLocation,
                                online = if (e.online!!) 1 else 0
                            )
                            DBFrames.instance.saveOrUpdate(bean)
                        }
                        Handler(Looper.getMainLooper()).postDelayed({
                            syncToDevice(result)
                        }, 500)
                        RxBus.getDefault().post(EventEntity(EventType.FRESH_DEVICE_LIST))
                    } else {
                        LoadingDialog.getInstance().dismiss()
                    }
                }

                override fun onError(e: ApiException) {
                    super.onError(e)
                    LoadingDialog.getInstance().dismiss()
                }

                override fun onComplete() {
                    super.onComplete()
                    RxBus.getDefault().post(EventEntity(EventType.UPDATE_DEVICE_LIST_COMPLETE))
                }
            })
    }

    fun syncToDevice(devices: List<FramesEntity>) {
        val syncDeviceList = devices.filter { it.id != mDeviceId }
        if(syncDeviceList.isEmpty()){
            return
        }
        val taskIds = items.map { it.data!!.id!! }
        val map = mutableMapOf<String, Any>()
        map["tasksId"] = taskIds
        map["deviceId"] = syncDeviceList.map { it.id }
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.addTasksDeviceRelation(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    if (!LoadingDialog.getInstance().isShowing()) {
                        LoadingDialog.getInstance().show()
                    }
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        ToastUtils.showShort(R.string.synchronization_is_successful)
                    } else {
                        ToastUtils.showShort(R.string.synchronization_failed)
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    LoadingDialog.getInstance().dismiss()
                }
            })
    }

}